﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace FGrubert.VpcManager
{
    /// <summary>
    /// Scanner that scans folders for virtual machines
    /// </summary>
    public class FolderScanner : IDisposable
    {
        /// <summary>
        /// Event that is raised when a new machine was found
        /// </summary>
        public event DlgMachineInformationChanged NewMachine;

        /// <summary>
        /// Event that is raised when a new folder is scanned
        /// </summary>
        public event DlgScanningFolder ScanningFolder;

        private bool recursive;
        /// <summary>
        /// Gets or sets if the folders should be scanned recursively (including subfolders) or not
        /// </summary>
        public bool Recursive
        {
            get { return recursive; }
            set { recursive = value; }
        }

        private List<string> folders;
        /// <summary>
        /// Gets or sets the list of folders to scan
        /// </summary>
        public List<string> Folders
        {
            get { return folders; }
            set { folders = value; }
        }

        /// <summary>
        /// Stores the file system watcher that are used for monitoring the folders
        /// </summary>
        private List<FileSystemWatcher> watcher;

        private bool monitorPermanently;
        /// <summary>
        /// Get if the paths are monitored permanently for new machines
        /// </summary>
        public bool MonitorPermanently
        {
            get { return monitorPermanently; }
        }

        /// <summary>
        /// Creates a new folder scanner
        /// </summary>
        /// <param name="folders">The folders to scan</param>
        /// <param name="recursive">If the folders should be scanned recursively (including subfolders) or not</param>
        /// <param name="monitorPermanently">If the paths are monitored permanently for new machines</param>
        public FolderScanner(IEnumerable<string> folders, bool recursive, bool monitorPermanently)
        {
            this.folders = new List<string>(folders);
            this.recursive = recursive;
            this.monitorPermanently = monitorPermanently;
        }

        /// <summary>
        /// Creates a new folder scanner
        /// </summary>
        /// <param name="folder">The folder to scan</param>
        public FolderScanner(string folder)
            : this(new List<string>(new string[] { folder }), true, true)
        {
        }

        /// <summary>
        /// Scans all folders for virtual machines
        /// </summary>
        /// <returns>The list of machines</returns>
        public List<Machine> ScanForMachines()
        {
            List<Machine> machines = new List<Machine>();

            // Disable old watcher
            if (this.watcher != null)
            {
                foreach (FileSystemWatcher myWatcher in this.watcher)
                {
                    myWatcher.EnableRaisingEvents = false;
                    myWatcher.Dispose();
                }
            }


            // Prepare new watcher
            if (this.monitorPermanently)
                this.watcher = new List<FileSystemWatcher>(this.folders.Count);
            else
                this.watcher = null;

            foreach (string folder in this.folders)
            {
                ScanFolder(ref machines, folder, this.recursive);

                // Create a watcher
                if (monitorPermanently)
                {
                    FileSystemWatcher myWatcher = new FileSystemWatcher(folder);
                    myWatcher.IncludeSubdirectories = this.recursive;
                    myWatcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;

                    myWatcher.Renamed += new RenamedEventHandler(myWatcher_Renamed);
                    myWatcher.Created += new FileSystemEventHandler(myWatcher_Created);

                    myWatcher.EnableRaisingEvents = true;

                    this.watcher.Add(myWatcher);
                }
            }

            return machines;
        }

        /// <summary>
        /// Raised when a file or a folder was renamed
        /// </summary>
        private void myWatcher_Renamed(object sender, RenamedEventArgs e)
        {
            if (File.Exists(e.FullPath))
            {
                // This is a file name change: we don't care. We only care for renamed folders
                return;
            }
            // TODO Implement the event

        }

        /// <summary>
        /// Raised when a new file was created in one of the monitored paths
        /// </summary>
        private void myWatcher_Created(object sender, FileSystemEventArgs e)
        {
            string extension = Path.GetExtension(e.FullPath);
            if (string.IsNullOrEmpty(extension) || !extension.Equals(".vmc", StringComparison.InvariantCultureIgnoreCase))
            {
                return;
            }
            if (this.NewMachine != null && !e.FullPath.Contains("$RECYCLE.BIN"))
            {
                Machine machine = new Machine(e.FullPath);
                this.NewMachine(machine);
            }
        }

        /// <summary>
        /// Scans the given folder for virtual machines
        /// </summary>
        /// <param name="machines">Gets the found maschines</param>
        /// <param name="myFolder">The folder to scan</param>
        /// <param name="myRecursive">If the scan should be continued recursively</param>
        private void ScanFolder(ref List<Machine> machines, string myFolder, bool myRecursive)
        {
            try
            {
                if (myFolder.EndsWith("$RECYCLE.BIN", StringComparison.InvariantCultureIgnoreCase))
                {
                    return;
                }

                if (ScanningFolder != null)
                {
                    ScanningFolder(myFolder);
                }

                // NOTE: you cannot use the search option "AllDirectories" because it throws an
                // UnauthorizedAccessexception as soon as it can not access a path
                string[] files = Directory.GetFiles(myFolder, "*.vmc", SearchOption.TopDirectoryOnly);
                foreach (string file in files)
                {
                    Machine newMachine = new Machine(file);
                    machines.Add(newMachine);
                    if (NewMachine != null)
                    {
                        NewMachine(newMachine);
                    }
                }

                if (recursive)
                {
                    string[] subFolders = Directory.GetDirectories(myFolder);
                    foreach (string subFolder in subFolders)
                    {
                        ScanFolder(ref machines, subFolder, myRecursive);
                    }
                }
            }
            catch (UnauthorizedAccessException)
            {
                // If you can't access a path just continue!
            }
        }

        #region IDisposable Member

        /// <summary>
        /// Disposed used ressources
        /// </summary>
        public void Dispose()
        {
            if (this.watcher != null)
            {
                foreach (FileSystemWatcher myWatcher in this.watcher)
                {
                    myWatcher.Dispose();
                }
            }
        }

        #endregion
    }
}
